﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using System.Windows.Forms;
using FeedAntGame.Control;
using FeedAntGame.Component;
using FeedAntGame.Sprite;
using FeedAntGame.Sprite.Event;
using FeedAntGame.Support;
using System.Collections;

namespace FeedAntGame.Bus
{
    /// <summary>
    /// size 684 x 560
    /// </summary>
    class MapCTL
    {
        Game game;
        private Texture2D background;
        private Vector2 topLeftPos;
        private int height;
        private int width;
        public enum ROAD_POINT_TYPE { ORANGE }
        int MAX_LEVEL = 2;
        int MAX_POWER = 0;
        public int currentLevel = 1;
        public float currentScore = 0;
        public float lastScore = 0;
        public float currentPower = 0;
        private int reDecreaseTime = 0;
        private int reComboTime = 0;
        private int comboCount = 0;
        private bool combo = false;
        private Vector2 comboPos = Vector2.Zero;
        private int lossPowerPerSecond = 5;
        private List<BaseSprite> listDeath = new List<BaseSprite>();
        private List<BaseSprite> listBarrier = new List<BaseSprite>();
        private List<BaseSprite> listFood = new List<BaseSprite>();
        
        private List<BaseSprite> listAnt = new List<BaseSprite>();
        private List<BaseSprite> checkPointList = new List<BaseSprite>();

        //Matrix field
        private List<BaseSprite> listRoad = new List<BaseSprite>();
        //Matrix way
        public  int MATRIX_WIDTH = 30;
        public  int MATRIX_HEIGHT = 20;
        public int[,] matrix = new int[30, 20];
        /************************************************************************/
        /*                  Get/Set                                                                     */
        /************************************************************************/

        public Game Game
        {
            get { return game; }
        }
        public Vector2 TopLeftPos
        {
            get { return topLeftPos; }
            set { topLeftPos = value; }
        }

        internal List<BaseSprite> ListFood
        {
            get { return listFood; }
            set { listFood = value; }
        }

        internal List<BaseSprite> ListDeath
        {
            get { return listDeath; }
            set { listDeath = value; }
        }

        internal List<BaseSprite> ListBarrier
        {
            get { return listBarrier; }
            set { listBarrier = value; }
        }

        public Texture2D Background
        {
            get { return background; }
            set { background = value; }
        }

        internal List<BaseSprite> ListSprite
        {
            get { return listDeath; }
            set { listDeath = value; }
        }
        
        public Vector2 Position
        {
            get { return topLeftPos; }
            set { topLeftPos = value; }
        }
        
        public int Height
        {
            get { return height; }
            set { height = value; }
        }
        

        public int Width
        {
            get { return width; }
            set { width = value; }
        }

        internal List<BaseSprite> ListRoad
        {
            get { return listRoad; }
            set { listRoad = value; }
        }

        /************************************************************************/
        /*                      Constructor                                                                     */
        /************************************************************************/
        public MapCTL(Game game,  Texture2D background,int height, int width, Vector2 position)
        {
            this.Background = background;
            this.Height = height;
            this.Width = width;
            this.Position = position;
            this.game = game;            
            generatePoint();
            clearMatrix();
            setReDecreaseTime(1000);
        }

        

        /************************************************************************/
        /*                      Main function                                                                     */
        /************************************************************************/
        public virtual void Update(GameTime gameTime, Rectangle clientBounds)
        {
            switch (((GameFA)game).playManager.gameState)
            {
                case GameState.LOADING:
                    {

                    }
                    break;
                case GameState.READY:
                    break;
                case GameState.INGAME:
                    {
                        //Decrease power per second
                        reDecreaseTime -= gameTime.ElapsedGameTime.Milliseconds;
                        if (reDecreaseTime <= 0)
                        {
                            setReDecreaseTime(1000);
                            this.currentPower -= lossPowerPerSecond;
                        }

                        //Check combo time
                        if (this.combo)
                        {
                            if (reComboTime > 0)
                            {
                                reComboTime -= gameTime.ElapsedGameTime.Milliseconds;
                            }
                            else if (comboCount >= 2)
                            {
                                //combo textSprite appear
                                ((GameFA)Game).playManager.InsertCombo(comboCount, comboPos);
                                this.currentScore += 100 * comboCount;
                                combo = false;
                            }
                        }
                        

                        foreach (BaseSprite s in listDeath)
                        {
                            s.Update(gameTime, clientBounds);
                        }

                        for (int i = 0; i < listBarrier.Count; i++)
                        {
                            BaseSprite s = (BaseSprite)listBarrier.ElementAt(i);
                            s.Update(gameTime, clientBounds);
                        }

                        foreach (BaseSprite s in checkPointList)
                        {
                            s.Update(gameTime, clientBounds);
                            for(int i = 0 ; i < listAnt.Count; i++)
                            {
                                BaseSprite antSprite = listAnt.ElementAt(i);
                                if (s.collisionRect.Intersects(antSprite.collisionRect))
                                {
                                    listAnt.Remove(antSprite);
                                }
                            }
                        }

                        for (int i = 0; i < listRoad.Count; i++)
                        {
                            BaseSprite s = (BaseSprite)listRoad.ElementAt(i);
                            s.Update(gameTime, clientBounds);
                            //remove road point if it intersect with barrier
                            for (int j = 0; j < listBarrier.Count; j++)
                            {
                                BaseSprite s1 = listBarrier.ElementAt(j);
                                if (s.collisionRect.Intersects(s1.CreateCollisionRect(new Vector2(0, s1.Height * 3 / 5)
                                        , s1.Width, s1.Height * 2 / 5)))
                                {
                                    listRoad.Remove(s);
                                }
                            }
                        }

                        for(int i = 0 ; i < listFood.Count; i++)
                        {
                            BaseSprite s = listFood.ElementAt(i);
                            s.Update(gameTime, clientBounds);
                            //check if it intersect with checkpoint
                            foreach (BaseSprite checkpoint in checkPointList)
                            {
                                if (s.collisionRect.Intersects(checkpoint.collisionRect))
                                {
                                    listFood.Remove(s);
                                }
                            }
                            
                        }

                        if (listAnt.Count == 0 && currentLevel < MAX_LEVEL )
                        {                            
                            ((GameFA)Game).playManager.gameState = GameState.NEXT_LEVEL;
                            this.InsertMidText(((GameFA)Game).playManager.TextList, AnimateText.TYPE.NEXT_LEVEL);
                            this.lastScore = this.currentScore;
                            return;
                        }
                        else if (listAnt.Count == 0 && currentLevel == MAX_LEVEL)
                        {
                            ((GameFA)Game).playManager.gameState = GameState.WIN;
                            this.InsertMidText(((GameFA)Game).playManager.TextList, AnimateText.TYPE.WIN);
                            return;
                        }
                        foreach (BaseSprite s2 in listAnt)
                        {
                            // check if ant intersect with check point
                            AntSprite s = (AntSprite)s2;
                            float min_distance = float.MaxValue;
                            BaseSprite near_candy = null;
                            // best way list exist
                            if (s.bestWayList.Count != 0)
                            {
                                Vector2 pos = s.bestWayList.ElementAt(s.bestWayList.Count - 1).Position;
                                if ((s as AntSprite).move(ref pos))
                                {
                                    s.bestWayList.Remove(s.bestWayList.ElementAt(s.bestWayList.Count - 1));
                                }
                                currentScore += s.ScroreValue;
                            }
                            else
                            {
                                near_candy = null;
                                //Find near candy
                                for (int i = 0; i < listFood.Count; i++)
                                {
                                    
                                    BaseSprite s1 = listFood.ElementAt(i);
                                    float distance = Vector2.Distance(s.Position, s1.Position);
                                    if (distance <= (s1 as CandySprite).Range)
                                    {
                                        if (distance < min_distance)
                                        {
                                            min_distance = distance;
                                            near_candy = s1;
                                        }

                                    }
                                    if (s.collisionRect.Intersects(s1.collisionRect))
                                    {
                                        currentScore += s1.ScroreValue;
                                        currentPower += (s1 as CandySprite).Power;
                                        listFood.Remove(s1);
                                        if (reComboTime <= 0)
                                        {
                                            setReCombo(2000);
                                        }
                                        comboCount += 1;
                                        comboPos = s.Position;
                                        ((GameFA)Game).PlayCue("coin");
                                    }

                                }
                                //Go to near candy
                                if (near_candy != null)
                                {
                                    Vector2 candyPos = near_candy.Position;
                                    (s as AntSprite).move(ref candyPos);
                                    currentScore += s.ScroreValue;
                                }

                                //If ant hit barrier, it find way to near candy
                                for (int i = 0; i < listBarrier.Count; i++)
                                {
                                    
                                    BaseSprite s1 = listBarrier.ElementAt(i);
                                    if (s.collisionRect.Intersects(s1.CreateCollisionRect(new Vector2(0,s1.Height*3/5)
                                        ,s1.Width ,s1.Height*2/5)))
                                    {
                                        ((GameFA)Game).PlayCue("tapglass");
                                        if (near_candy != null)
                                        {
                                            BaseSprite start = findNearRoad(s);
                                            BaseSprite end = findNearRoad(near_candy);
                                            if (s.floodList.Count == 0 && s.bestWayList.Count == 0)
                                            {
                                                s.FindWay(findNearRoad(start), findNearRoad(end),this);

                                            }
                                        }
                                    }
                                }
                            }


                            s.Update(gameTime, clientBounds);
                            if (s.bestWayList.Count == 0)
                            {
                                s.floodList.Clear();
                                s.clearMatrix();
                            }
                        }
                        
                    }
                    break;
                case GameState.NEXT_LEVEL:

                    break;
                case GameState.GAMEOVER:

                    break;
                case GameState.WIN:

                    break;
            }
            
        }

        public virtual void Draw(GameTime gameTime, SpriteBatch spriteBatch)
        {
            switch (((GameFA)game).playManager.gameState)
            {
                case GameState.LOADING:                    
                    spriteBatch.Draw(Background, new Rectangle((int)Position.X, (int)Position.Y, Width, Height),
                        Color.White);

                    foreach (BaseSprite s in listDeath)
                    {
                        s.Draw(gameTime, spriteBatch);
                    }

                    foreach (BaseSprite s in listBarrier)
                    {
                        s.Draw(gameTime, spriteBatch);
                    }                    
                    break;
                case GameState.READY:
                    spriteBatch.Draw(Background, new Rectangle((int)Position.X, (int)Position.Y, Width, Height),
                            Color.White);

                    foreach (BaseSprite s in listDeath)
                    {
                        s.Draw(gameTime, spriteBatch);
                    }

                    foreach (BaseSprite s in listBarrier)
                    {
                        s.Draw(gameTime, spriteBatch);
                    }

                    foreach (BaseSprite s in checkPointList)
                    {
                        s.Draw(gameTime, spriteBatch);
                    }

                    foreach (BaseSprite s in listAnt)
                    {
                        s.Draw(gameTime, spriteBatch);
                    }
                    break;
                case GameState.INGAME:
                    {
                        spriteBatch.Draw(Background, new Rectangle((int)Position.X, (int)Position.Y, Width, Height),
                            Color.White);

                        foreach (BaseSprite s in listDeath)
                        {
                            s.Draw(gameTime, spriteBatch);
                        }


                        foreach (BaseSprite s in listFood)
                        {
                            s.Draw(gameTime, spriteBatch);
                        }

                        foreach (BaseSprite s in listAnt)
                        {
                            s.Draw(gameTime, spriteBatch);
                        }

                        foreach (BaseSprite s in listBarrier)
                        {
                            s.Draw(gameTime, spriteBatch);
                        }

                        //be on debug
                        for (int i = 0; i < listRoad.Count; i++)
                        {
                            BaseSprite s1 = (BaseSprite)listRoad.ElementAt(i);

                            //               s1.Draw(gameTime, spriteBatch);
                            //                 spriteBatch.DrawString(((GameFA)game).fpsFont, matrix[(int)getPosInMatrix(s1).X, (int)getPosInMatrix(s1).Y].ToString(),
                            //                     s1.Position, MyColor.White);
                        }

                        foreach (BaseSprite s in checkPointList)
                        {
                            s.Draw(gameTime, spriteBatch);
                        }

                        
                    }
                    break;
                case GameState.NEXT_LEVEL:
                    spriteBatch.Draw(Background, new Rectangle((int)Position.X, (int)Position.Y, Width, Height),
                            Color.White);

                    break;
                case GameState.GAMEOVER:
                    break;
                case GameState.WIN:
                     spriteBatch.Draw(Background, new Rectangle((int)Position.X, (int)Position.Y, Width, Height),
                            Color.White);

                    foreach (BaseSprite s in listDeath)
                    {
                        s.Draw(gameTime, spriteBatch);
                    }

                    foreach (BaseSprite s in listBarrier)
                    {
                        s.Draw(gameTime, spriteBatch);
                    }

                    foreach (BaseSprite s in checkPointList)
                    {
                        s.Draw(gameTime, spriteBatch);
                    }

                    foreach (BaseSprite s in listAnt)
                    {
                        s.Draw(gameTime, spriteBatch);
                    }
                    break;
            }
            
        }

        /************************************************************************/
        /*                      Event                                                                     */
        /************************************************************************/
        

        /************************************************************************/
        /*                      Other methods                                                                     */
        /************************************************************************/

        public void SetBaseRoad(ROAD_POINT_TYPE type, params Vector2[] listPosition)
        {
            // TODO: add code here
            
            for (int i = 0; i < listPosition.GetLength(0); i++)
            {
                Vector2 pos = listPosition[i];
                pos += this.Position;                
                switch (type)
                {
                    case ROAD_POINT_TYPE.ORANGE:
                        AutomatedSprite point = new AutomatedSprite(
                            game.Content.Load<Texture2D>(@"Images\light_ball"), pos, new Point(24, 24),3, new Point(0, 0),
                            new Point(1, 1), Vector2.Zero, 120, null, 0,1);
                        point.Origin = new Vector2(point.TextureImage.Width / 2, point.TextureImage.Height / 2);
                        point.CollisionRectImage = game.Content.Load<Texture2D>(@"Images\collisionRect");
                        listRoad.Add(point);
                        break;
                }
                
            }
        }
        public bool SpawnFood(BaseSprite food, Vector2 position)
        {
            bool ret = false;
            if (CoverPosition(position))
            {
                // TODO: add code here
                food.Position = position;
                this.ListFood.Add(food);
                ret = true;
            }
            return ret;
        }

        /// <summary>
        /// Check if position is in mapInfo area
        /// </summary>
        /// <param name="position"></param>
        /// <returns></returns>
        public bool CoverPosition(Vector2 position)
        {
            return position.X > this.topLeftPos.X && position.X < this.topLeftPos.X + Width
                            && position.Y > this.topLeftPos.Y && position.Y < this.topLeftPos.Y + Height;
        }
        public Ant SpawnAnt(Game game, Ant.TYPE type, params Vector2[] position)
        {
            Ant ret = null;
            for (int i = 0; i < position.Length; i++)
            {
                if (position[i].X < this.topLeftPos.X + Width && position[i].Y < this.topLeftPos.Y + Height)
                {
                    // TODO: add code here
                    position[i] += this.Position;
                    ret = new Ant(game, type, position[i]);
                    listAnt.Add(ret.Sprite);
                }
            }
            return ret;
        }
        public Barrier[] SpawnStopObject(Game game, Barrier.TYPE type, params Vector2[] position)
        {
            List<Barrier> listAutomatedSprite = new List<Barrier>();
            for (int i = 0; i < position.Length; i++)
            {
                if (position[i].X < this.topLeftPos.X + Width && position[i].Y < this.topLeftPos.Y + Height)
                {
                    // TODO: add code here
                    listAutomatedSprite.Add(SpawnStopObject(game, type, position[i]));
                }
            }
            return listAutomatedSprite.ToArray();
        }
        public Barrier SpawnStopObject(Game game, Barrier.TYPE type, Vector2 position)
        {
            Barrier ret = null;
            position += this.Position;
            if (position.X < this.topLeftPos.X + Width && position.Y < this.topLeftPos.Y + Height)
            {
                // TODO: add code here
                Barrier barrier = new Barrier(game, type, position);
                //BaseSprite s = barrier.Sprite;
                //s.CollisionRectImage = game.Content.Load<Texture2D>(@"Images\collisionRect");
                listBarrier.Add(barrier.Sprite);
                ret = barrier;
            }
            return ret;
        }
        public CheckPointSprite SpawnCheckPoint(Game game, CheckPoint.TYPE type, Vector2 position)
        {
            CheckPointSprite ret = null;
            position += this.Position;
            if (position.X < this.topLeftPos.X + Width && position.Y < this.topLeftPos.Y + Height)
            {
                // TODO: add code here
                CheckPoint checkPoint = new CheckPoint(Game, CheckPoint.TYPE.TWITTER, position);                
                checkPointList.Add(checkPoint.Sprite);
                ret = (checkPoint.Sprite as CheckPointSprite);
            }
            return ret;
        }

        /// <summary>
        /// matrix size (MATRIX_WIDTH * MATRIX_HEIGHT )
        /// </summary>
        private void generatePoint()
        {
            for (int i = 0; i < MATRIX_HEIGHT; i++)
            {
                for (int j = 0; j < MATRIX_WIDTH; j++)
                {
                    SetBaseRoad(MapCTL.ROAD_POINT_TYPE.ORANGE,
                            new Vector2(j * 22,i * 30));
                }
            }
        }

        
        /// <summary>
        /// Find near road point. It combines with FindWay.
        /// </summary>
        /// <param name="sprite"></param>
        /// <returns></returns>
        private BaseSprite findNearRoad(BaseSprite sprite)
        {
            float min_distance = float.MaxValue;
            BaseSprite ret = null;
            foreach (BaseSprite s in listRoad)
            {
                float temp_distance = Vector2.Distance(s.Position, sprite.Position);
                
                if (temp_distance <min_distance)
                {
                    min_distance = temp_distance;
                    ret = s;
                }
            }
            return ret;
        }

        
        private ArrayList getMapInfo(String mapName)
        {
            ArrayList mapInfo = Database.Map.GetMap(mapName);
            return mapInfo;
        }

        private void spawnMap(ArrayList mapInfo)
        {
            String mapName = mapInfo[0].ToString();
            String imagePath = mapInfo[1].ToString();

            Point[] barrierPos = (Point[] )mapInfo[2];
            String[] barrierType = (String[])mapInfo[3];
            Point[] desPos = (Point[])mapInfo[4];
            String[] desType = (String[])mapInfo[5];
            Point[] dangerPos = (Point[] )mapInfo[6];
            String[] dangerType = (String[] )mapInfo[7];
            Point[] antPos = (Point[] )mapInfo[8];
            String[] antType = (String[] )mapInfo[9];

            //Spawn background
            Background = Game.Content.Load<Texture2D>(imagePath);

            //Spawn barriers
            for (int i = 0; i < barrierPos.Count(); i++)
            {
                Barrier.TYPE type = (Barrier.TYPE)Int32.Parse(barrierType[i]);
                SpawnStopObject(Game, (Barrier.TYPE)Int32.Parse(barrierType[i]), new Vector2(barrierPos[i].X, barrierPos[i].Y));
            }
            //Spawn destinations
            for (int i = 0; i < desPos.Count(); i++)
            {
                SpawnCheckPoint(Game, (CheckPoint.TYPE)Int32.Parse(desType[i]), new Vector2(desPos[i].X, desPos[i].Y));
            }
            //Spawn dangers
            for (int i = 0; i < dangerPos.Count(); i++)
            {
                SpawnCheckPoint(Game, (CheckPoint.TYPE)Int32.Parse(dangerType[i]), new Vector2(dangerPos[i].X, dangerPos[i].Y));
            }
            //Spawn ants
            MAX_POWER = 0;
            for (int i = 0; i < antPos.Count(); i++)
            {
                MAX_POWER += (SpawnAnt(Game, (Ant.TYPE)Int32.Parse(antType[i]), new Vector2(antPos[i].X, antPos[i].Y)).Sprite as
                    AntSprite).Power;
            }

            //another map info
            switch (mapName)
            {
                case "1":
                    currentPower = MAX_POWER;
                    this.lossPowerPerSecond = 5;   
                    break;
                case "2":
                    currentPower = MAX_POWER;
                    this.lossPowerPerSecond = 10;   
                    break;
            }

        }
        public bool GetMap(int level)
        {
            
            bool ret = false;
            Reset();
            switch (level)
            {
                case 1:

                    currentLevel = level;
                    //createMapGreen();
                    spawnMap(getMapInfo(level.ToString()));                   
                    ret = true;
                    break;
                case 2:
                    currentLevel = level;
                    spawnMap(getMapInfo(level.ToString())); 
                    ret = true;
                    break;
            }            
            return ret;
        }

        //create Map
        private void createMapGreen()
        {
            String mapName = "1";
            String imagePath = @"Images\map_green";
            
            List<Point> barrierPos  = new List<Point>();
            List<String> barrierType  = new List<String>();
            List<Point> desPos = new List<Point>();
            List<String> desType = new List<String>();
            List<Point> dangerPos = new List<Point>();
            List<String> dangerType = new List<String>();
            List<Point> antPos = new List<Point>();
            List<String> antType = new List<String>();
            Background = Game.Content.Load<Texture2D>(@"Images\map_green");
            for (int i = 0; i < 6; i++)
            {
                SpawnStopObject((GameFA)Game, Barrier.TYPE.ROCK4, new Vector2(i * 70, 136));
                barrierPos.Add(new Point(i * 70, 136));
                int type = (int)Barrier.TYPE.ROCK4;
                barrierType.Add(type.ToString());
            }
            for (int i = 0; i < 8; i++)
            {
                SpawnStopObject((GameFA)Game, Barrier.TYPE.ROCK4, new Vector2(85 + i * 70, 400));
                barrierPos.Add(new Point(85 + i * 70, 400));
                int type = (int)Barrier.TYPE.ROCK4;
                barrierType.Add(type.ToString());
            }
            SpawnCheckPoint(Game, CheckPoint.TYPE.TWITTER, new Vector2(Width / 2, Height / 2));
            desPos.Add(new Point(Width / 2, Height / 2));
            int type1 = (int)CheckPoint.TYPE.TWITTER;
            desType.Add(type1.ToString());
            
            MAX_POWER = (SpawnAnt(Game, Ant.TYPE.PINK, new Vector2(20, 20)).Sprite as AntSprite).Power;
            MAX_POWER += (SpawnAnt(Game, Ant.TYPE.PINK, new Vector2(Width - 20, Height - 20)).Sprite as AntSprite).Power;
            antPos.Add(new Point(20, 20));
            type1 = (int)Ant.TYPE.PINK;
            antType.Add(type1.ToString());
            antPos.Add(new Point(Width - 20, Height - 20));
            type1 = (int)Ant.TYPE.PINK;
            antType.Add(type1.ToString());

            Database.Map.LuuMap(mapName, imagePath, barrierPos.ToArray(), barrierType.ToArray(), desPos.ToArray(),
                desType.ToArray(), dangerPos.ToArray(), dangerType.ToArray(), antPos.ToArray(), antType.ToArray());

            currentPower = MAX_POWER;
            this.lossPowerPerSecond = 5;           
            
        }


        //get mapInfo
        
        public void Reset()
        {
            listDeath.Clear();
            listAnt.Clear();
            listBarrier.Clear();
            listFood.Clear();
            checkPointList.Clear();
            currentPower = 0;
        }
        public void clearMatrix()
        {
            for (int i = 0; i < matrix.GetLength(0); i++)
            {
                for (int j = 0; j < matrix.GetLength(1); j++)
                {
                    matrix[i, j] = 0;
                }
            }
        }

        /// <summary>
        /// Get percent power remaining
        /// </summary>
        public float GetPowerRemain()
        {
            if (this.currentPower > this.MAX_POWER)
            {
                this.currentPower = this.MAX_POWER;
            }
            return this.currentPower / this.MAX_POWER;
        }

        /// <summary>
        /// Scale: miliSeconds
        /// </summary>
        /// <param name="distanceTime"></param>
        private void setReDecreaseTime(int distanceTime)
        {
            this.reDecreaseTime = distanceTime;
        }

        private void setReCombo(int distanceTime)
        {
            this.reComboTime = distanceTime;
            this.comboCount = 0;
            this.comboPos = Vector2.Zero;
            this.combo = true;
        }

        public void InsertMidText(List<AnimateText> textList , AnimateText.TYPE type)
        {
            AnimateText text = new AnimateText(Game, type, Vector2.Zero);
            text.Sprite.Position = this.TopLeftPos +  new Vector2(Width / 2 - text.Sprite.Width / 2, Height / 2 - text.Sprite.Height / 2);
            if (text.Sprite != null)
            {
                textList.Add(text);
            }
        }
    }
}

